package com.tanjie.blackbean.web.auth;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.tanjie.blackbean.core.auth.PermissionValidator;
import com.tanjie.blackbean.core.util.TokenUtil;
import com.tanjie.blackbean.pojo.auth.AuthConst;
import com.tanjie.blackbean.pojo.auth.UserInfo;
import com.tanjie.blackbean.pojo.exception.AuthException;
import com.tanjie.blackbean.pojo.properties.BlackbeanAuthProperties;
import com.tanjie.blackbean.pojo.response.AuthErrorCode;
import com.tanjie.blackbean.pojo.web.BlackbeanHttpHeaders;
import com.tanjie.blackbean.web.annotation.HasPermission;
import com.tanjie.blackbean.web.annotation.HasRole;
import com.tanjie.blackbean.web.support.AnnotationExtractor;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.tanjie.blackbean.web.support.UrlSkipUtil.annotationSkipCheck;
import static com.tanjie.blackbean.web.support.UrlSkipUtil.skipUrlCheck;
import static com.tanjie.blackbean.web.log.LogPrinter.LOG_MDC_KEY;

/**
 * 拦截所有请求
 *
 * @author: tanjie
 * @date: 2022-04-25
 **/
@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Resource
    BlackbeanAuthProperties authProperties;

    @Resource
    PermissionValidator permissionValidator;

    @Override
    public boolean preHandle(@NonNull HttpServletRequest request,
                             @NonNull HttpServletResponse response,
                             @NonNull Object handler) {
        //
        String logThreadId = UUID.fastUUID().toString(true);
        MDC.put(LOG_MDC_KEY, logThreadId);
        // 跳过请求路径
        String uri = request.getRequestURI();
        String method = request.getMethod();
        if (skipUrlCheck(uri, authProperties.getSkipUrl(), method)) {
            return true;
        }
        // 跳过feign和skipAuth
        if (annotationSkipCheck(uri, handler)) {
            return true;
        }
        // token校验
        String token = request.getHeader(BlackbeanHttpHeaders.BLACKBEAN_TOKEN);
        if (StrUtil.isBlank(token)) {
            throw new AuthException(AuthErrorCode.TOKEN_BLANK);
        }
        // 校验、解码token将token信息转换成userInfo.
        UserInfo userInfo;
        try {
            userInfo = TokenUtil.parseUserInfo(token);
            log.info("userinfo from token = {}", userInfo);
            request.setAttribute(AuthConst.USER_INFO, userInfo);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("解析token失败，cause:{}", e.getMessage());
            throw new AuthException(AuthErrorCode.TOKEN_INVALID);
        }
        // 角色、权限校验
        permissionVerify(userInfo, handler);
        return true;
    }

    @Override
    public void postHandle(@NonNull HttpServletRequest request,
                           @NonNull HttpServletResponse response,
                           @NonNull Object handler,
                           ModelAndView modelAndView) {
    }

    @Override
    public void afterCompletion(@NonNull HttpServletRequest request,
                                @NonNull HttpServletResponse response,
                                @NonNull Object handler,
                                Exception ex) {
        MDC.remove(LOG_MDC_KEY);
    }

    private void permissionVerify(UserInfo userInfo, Object handler) {
        if (handler instanceof HandlerMethod) {
            List<String> requiredRoles = new ArrayList<>();
            List<String> requiredPermissions = new ArrayList<>();
            HasRole hasRole = AnnotationExtractor.get((HandlerMethod) handler, HasRole.class);
            if (hasRole != null) {
                requiredRoles = Arrays.asList(hasRole.value());
            }
            HasPermission hasPermission = AnnotationExtractor.get((HandlerMethod) handler, HasPermission.class);
            if (hasPermission != null) {
                requiredPermissions = Arrays.asList(hasPermission.value());
            }
            permissionValidator.verify(userInfo.getId(), requiredRoles, requiredPermissions);
        }
    }

}
